package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.enums.user.IsVipEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.UserListenProcessFeignClient;
import com.atguigu.tingshu.user.client.UserPaidAlbumFeignClient;
import com.atguigu.tingshu.user.client.UserPaidTrackFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.order.TrackOrderVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private VodConstantProperties vodConstantProperties;

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private VodService vodService;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private UserPaidAlbumFeignClient userPaidAlbumFeignClient;

    @Autowired
    private UserPaidTrackFeignClient userPaidTrackFeignClient;

    @Autowired
    private UserListenProcessFeignClient userListenProcessFeignClient;

    @Override
    @Transactional
    public void saveTrackInfo(TrackInfoVo vo) {
        TrackInfo trackInfo = new TrackInfo();
        BeanUtils.copyProperties(vo, trackInfo);
        trackInfo.setUserId(AuthContextHolder.getUserId());
        // 查询上一条音频的order_num，设置该条音频的order_num
        TrackInfo preTrackInfo = this.lambdaQuery()
                .eq(TrackInfo::getAlbumId, trackInfo.getAlbumId())
                .orderByDesc(TrackInfo::getOrderNum)
                .select(TrackInfo::getOrderNum)
                .last("limit 1").one();
        trackInfo.setOrderNum(preTrackInfo == null ? 1 : preTrackInfo.getOrderNum() + 1);
        // 设置媒体相关参数
        TrackMediaInfoVo trackMediaInfo = vodService.getTrackMediaInfo(vo.getMediaFileId());
        // 设置参数
        trackInfo.setMediaDuration(new BigDecimal(trackMediaInfo.getDuration()));
        trackInfo.setMediaSize(trackMediaInfo.getSize());
        trackInfo.setMediaType(trackMediaInfo.getType());
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        trackInfoMapper.insert(trackInfo);
        // 更新专辑声音总数
        Db.lambdaUpdate(AlbumInfo.class)
                .eq(AlbumInfo::getId, trackInfo.getAlbumId())
                .setSql("include_track_count = include_track_count + 1")
                .update();
        // 初始化声音统计
        this.saveTrackStat(trackInfo.getId());
    }

    @Override
    public IPage<TrackListVo> findUserTrackPage(Integer pageNum, Integer pageSize, TrackInfoQuery trackInfoQuery) {
        Page<TrackListVo> page = new Page<>(pageNum, pageSize);
        Long userId = AuthContextHolder.getUserId();
        trackInfoQuery.setUserId(userId);
        IPage<TrackListVo> voIPage = trackInfoMapper.findUserTrackPage(page, trackInfoQuery);
        return voIPage;
    }

    @Override
    @Transactional
    public void updateTrackInfo(Long id, TrackInfoVo trackInfoVo) {
        TrackInfo trackInfo = this.getById(id);
        String oldMediaFileId = trackInfo.getMediaFileId();
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        String newMediaFileId = trackInfoVo.getMediaFileId();
        // 判断音频信息是否有修改
        // todo: 如果修改了音频文件，需要将原来的音频文件删除后再新增音频
        if (!newMediaFileId.equals(oldMediaFileId)) {
            TrackMediaInfoVo trackMediaInfo = vodService.getTrackMediaInfo(newMediaFileId);
            trackInfo.setMediaDuration(new BigDecimal(trackMediaInfo.getDuration()));
            trackInfo.setMediaSize(trackMediaInfo.getSize());
            trackInfo.setMediaType(trackMediaInfo.getType());
            trackInfo.setMediaUrl(trackMediaInfo.getMediaUrl());
        }
        this.updateById(trackInfo);
    }

    @Override
    @Transactional
    public void removeTrackInfo(Long id) {
        TrackInfo trackInfo = this.getById(id);
        this.removeById(id);
        Db.remove(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, id));
        Db.lambdaUpdate(AlbumInfo.class)
                .eq(AlbumInfo::getId, trackInfo.getAlbumId())
                .setSql("include_track_count = include_track_count - 1")
                .update();
        vodService.removeTrackMedia(trackInfo.getMediaFileId());
    }

    @Override
    public IPage<AlbumTrackListVo> findAlbumTrackPage(Long albumId, Page<AlbumTrackListVo> page) {
        IPage<AlbumTrackListVo> iPage = trackInfoMapper.findAlbumTrackPage(albumId, page);
        List<AlbumTrackListVo> records = iPage.getRecords();
        // 判断该专辑是否是免费专辑
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        if (albumInfo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_FREE)) {
            return iPage;
        } else {
            // 去除免费试听的声音列表，获取本页需要付费的声音列表
            List<AlbumTrackListVo> needPaidTrackList = records.stream().filter(track ->
                    track.getOrderNum().intValue() > albumInfo.getTracksForFree()).collect(Collectors.toList());
            // 判断需要付费的声音列表是否为空
            if (CollUtil.isEmpty(needPaidTrackList)) {
                return iPage;
            } else {
                Long userId = AuthContextHolder.getUserId();
                // 判断用户是否登录，若未登录，不免费的声音均显示收费
                if (userId == null) {
                    records.forEach(item -> {
                        if (item.getOrderNum().intValue() > albumInfo.getTracksForFree()) {
                            item.setIsShowPaidMark(true);
                        }
                    });
                    iPage.setRecords(records);
                    return iPage;
                } else {
                    Result<UserInfoVo> userResult = userInfoFeignClient.getUserInfoVo(userId);
                    Assert.notNull(userResult, "查询id为" + userId + "的用户信息为空");
                    UserInfoVo user = userResult.getData();
                    // 如果该专辑是当前登录用户自己发布的，免费
                    if (albumInfo.getUserId().equals(userId)) return iPage;
                    // 判断专辑是否为vip免费并且当前用户vip未过期
                    if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(albumInfo.getPayType())
                            && user.getIsVip().intValue() == IsVipEnum.IS_VIP.getCode()
                            && user.getVipExpireTime().after(new Date())) {
                        return iPage;
                    }
                    Result<UserPaidAlbum> userPaidAlbumResult = userPaidAlbumFeignClient.findUserPaidAlbum(userId, albumId);
                    Assert.notNull(userPaidAlbumResult, "服务异常");
                    UserPaidAlbum userPaidAlbum = userPaidAlbumResult.getData();
                    // 判断用户是否购买过该专辑
                    if (userPaidAlbum != null) return iPage;
                    // 遍历收费声音列表，判断用户哪几集未购买过
                    Result<List<UserPaidTrack>> userPaidTracksResult = userPaidTrackFeignClient.findUserPaidTrack(userId, albumId);
                    Assert.notNull(userPaidTracksResult, "服务异常");
                    List<UserPaidTrack> userPaidTracks = userPaidTracksResult.getData();
                    if (CollUtil.isEmpty(userPaidTracks)) {
                        needPaidTrackList.forEach(item -> item.setIsShowPaidMark(true));
                    } else {
                        List<Long> userPaidTrackIds = userPaidTracks.stream().map(item -> item.getTrackId()).collect(Collectors.toList());
                        needPaidTrackList.forEach(item -> {
                            if (!userPaidTrackIds.contains(item.getTrackId())) {
                                item.setIsShowPaidMark(true);
                            }
                        });
                    }
                    iPage.setRecords(records);
                }
            }
        }
        return iPage;
    }

    @Override
    public TrackStatVo getTrackStatVo(Long trackId) {
        TrackStatVo vo = trackInfoMapper.getTrackStat(trackId);
        return vo;
    }

    @Override
    public Map<String, Object> getPlayToken(Long trackId) {
        // 声音
        TrackInfo trackInfo = this.getById(trackId);
        // 专辑
        AlbumInfo albumInfo = Db.lambdaQuery(AlbumInfo.class).eq(AlbumInfo::getId, trackInfo.getAlbumId()).one();
        // 判断当前用户是否可以播放该声音
        if (check(trackInfo, albumInfo)) {
            // 获取当前的播放进度
            Result<BigDecimal> result = userListenProcessFeignClient.getTrackBreakSecond(trackId);
            Assert.notNull(result, "获取到跳出时间结果不能为空");
            BigDecimal breakSecond = result.getData();
            String playToken = vodService.getPlayToken(trackInfo.getMediaFileId());
            Map<String, Object> map = new HashMap<>();
            map.put("playToken", playToken);
            map.put("mediaFileId", trackInfo.getMediaFileId());
            map.put("breakSecond", breakSecond);
            map.put("appId", vodConstantProperties.getAppId());
            //获取下一个播放声音
            LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId());
            queryWrapper.gt(TrackInfo::getOrderNum, trackInfo.getOrderNum());
            queryWrapper.orderByAsc(TrackInfo::getOrderNum);
            queryWrapper.select(TrackInfo::getId);
            queryWrapper.last("limit 1");
            TrackInfo nextTrackInfo = this.getOne(queryWrapper);
            if (null != nextTrackInfo) {
                map.put("nextTrackId", nextTrackInfo.getId());
            } else {
                map.put("nextTrackId", 0L);
            }
            return map;
        }
        return null;
    }

    @Override
    public List<TrackOrderVo> findUserTrackPaidList(Long trackId) {
        // 查询该声音是否存在
        TrackInfo trackInfo = this.getById(trackId);
        if (trackInfo == null) throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        // 根据专辑id，查询同一专辑下本集及本集以后的声音列表
        Long albumId = trackInfo.getAlbumId();
        List<TrackInfo> trackInfos = this.lambdaQuery()
                .eq(TrackInfo::getAlbumId, albumId)
                .ge(TrackInfo::getOrderNum, trackInfo.getOrderNum()).list();
        Long userId = AuthContextHolder.getUserId();
        // 查询用户在该专辑中已购买的声音
        Result<List<UserPaidTrack>> userPaidTracksResult = userPaidTrackFeignClient.findUserPaidTrack(userId, albumId);
        Assert.notNull(userPaidTracksResult, "用户的声音购买查询失败");
        List<Long> paidTrackIds = userPaidTracksResult.getData().stream().map(item -> item.getTrackId()).collect(Collectors.toList());
        // 过滤用户在该专辑中未购买的声音列表
        trackInfos = trackInfos.stream().filter(item -> !paidTrackIds.contains(item.getId())).collect(Collectors.toList());
        if (CollUtil.isEmpty(trackInfos)) throw new GuiguException(ResultCodeEnum.SERVICE_ERROR);
        AlbumInfo albumInfo = Db.lambdaQuery(AlbumInfo.class).eq(AlbumInfo::getId, albumId).one();
        int size = trackInfos.size();
        List<TrackOrderVo> resultList = new ArrayList<>();
        // size = 1
        resultList.add(TrackOrderVo.builder()
                .name("本集")
                .price(albumInfo.getPrice())
                .trackCount(1)
                .build());
        // size >= 10
        if (size >= 10) {
            resultList.add(TrackOrderVo.builder()
                    .name("后10集")
                    .price(albumInfo.getPrice().multiply(new BigDecimal(10)))
                    .trackCount(10)
                    .build());
        }
        // size >= 20
        if (size >= 20) {
            resultList.add(TrackOrderVo.builder()
                    .name("后20集")
                    .price(albumInfo.getPrice().multiply(new BigDecimal(20)))
                    .trackCount(20)
                    .build());
        }
        // size >= 50
        if (size >= 50) {
            resultList.add(TrackOrderVo.builder()
                    .name("后50集")
                    .price(albumInfo.getPrice().multiply(new BigDecimal(50)))
                    .trackCount(50)
                    .build());
        }
        // size > 1
        if (size > 1) {
            resultList.add(TrackOrderVo.builder()
                    .name("后" + size + "集")
                    .price(albumInfo.getPrice().multiply(new BigDecimal(size)))
                    .trackCount(size)
                    .build());
        }
        return resultList;
    }

    @Override
    public List<TrackInfo> findUserNeedPaidTracks(Long trackId, Integer count) {
        TrackInfo trackInfo = this.getById(trackId);
        Long userId = AuthContextHolder.getUserId();
        Long albumId = trackInfo.getAlbumId();
        Result<List<UserPaidTrack>> userPaidTracksResult = userPaidTrackFeignClient.findUserPaidTrack(userId, albumId);
        Assert.notNull(userPaidTracksResult, "查询用户在该专辑下购买的声音集合失败");
        List<UserPaidTrack> userPaidTracks = userPaidTracksResult.getData();
        List<Long> userPaidTrackIds = null;
        if (CollUtil.isNotEmpty(userPaidTracks)) {
            userPaidTrackIds = userPaidTracks.stream().map(item -> item.getTrackId()).collect(Collectors.toList());
        }
        List<TrackInfo> trackInfos = this.lambdaQuery().eq(TrackInfo::getAlbumId, albumId)
                .ge(TrackInfo::getOrderNum, trackInfo.getOrderNum())
                .notIn(CollUtil.isNotEmpty(userPaidTrackIds), TrackInfo::getId, userPaidTrackIds)
                .last("limit " + count)
                .list();
        return trackInfos;
    }

    /**
     * 判断当前用户是否可以播放该声音
     *
     * @param trackInfo
     * @param albumInfo
     * @return
     */
    private boolean check(TrackInfo trackInfo, AlbumInfo albumInfo) {
        // 该专辑是免费专辑，或该集声音属于免费试听集数内
        if (SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())
                || trackInfo.getOrderNum() <= albumInfo.getTracksForFree()) {
            return true;
        }
        Long userId = AuthContextHolder.getUserId();
        // 判断操作用户是否登录
        if (userId == null) {
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        // 判断该专辑是否是登录用户自己上传的
        if (albumInfo.getUserId().equals(userId)) {
            return true;
        }
        // 判断该专辑是否是vip免费并且登录用户的vip未过期
        Result<UserInfoVo> userInfoResult = userInfoFeignClient.getUserInfoVo(userId);
        Assert.notNull(userInfoResult, "获取id为【" + userId + "】的用户信息失败");
        UserInfoVo userInfo = userInfoResult.getData();
        if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(albumInfo.getPayType())
                && userInfo.getVipExpireTime().after(new Date())) {
            return true;
        }
        // 判断登录用户是否购买了该专辑
        Long albumId = albumInfo.getId();
        Result<UserPaidAlbum> userPaidAlbumResult = userPaidAlbumFeignClient.findUserPaidAlbum(userId, albumId);
        Assert.notNull(userInfoResult, "获取用户id为【" + userId + "】，专辑id为【" + albumId + "】的专辑购买信息失败");
        if (userPaidAlbumResult.getData() != null) {
            return true;
        }
        // 判断登录用户是否购买了该集声音
        Result<List<UserPaidTrack>> userPaidTracksResult = userPaidTrackFeignClient.findUserPaidTrack(userId, albumId);
        Assert.notNull(userInfoResult, "获取用户id为【" + userId + "】，专辑id为【" + albumId + "】的声音购买信息失败");
        if (userPaidTracksResult.getData() != null) {
            return true;
        }
        return false;
    }

    private void saveTrackStat(Long trackId) {
        List<TrackStat> list = new ArrayList<>();
        TrackStat trackStat1 = new TrackStat();
        trackStat1.setTrackId(trackId);
        trackStat1.setStatType(SystemConstant.TRACK_STAT_PLAY);
        trackStat1.setStatNum(0);
        list.add(trackStat1);
        TrackStat trackStat2 = new TrackStat();
        trackStat2.setTrackId(trackId);
        trackStat2.setStatType(SystemConstant.TRACK_STAT_COLLECT);
        trackStat2.setStatNum(0);
        list.add(trackStat2);
        TrackStat trackStat3 = new TrackStat();
        trackStat3.setTrackId(trackId);
        trackStat3.setStatType(SystemConstant.TRACK_STAT_PRAISE);
        trackStat3.setStatNum(0);
        list.add(trackStat3);
        TrackStat trackStat4 = new TrackStat();
        trackStat4.setTrackId(trackId);
        trackStat4.setStatType(SystemConstant.TRACK_STAT_COMMENT);
        trackStat4.setStatNum(0);
        list.add(trackStat4);
        Db.saveBatch(list);
    }
}
